/*
 *  SoundInfo.mm
 *  SoundEngine
 *
 *  Created by handcn on 09-3-10.
 *  Copyright 2009 __MyCompanyName__. All rights reserved.
 *
 */

#include "SoundInfo.h"
#include "OpenALSupport.h"

#define SOUND_BUFFER_SIZE 32768//65535

#ifndef kAudioEngine_Debug
//	#define kAudioEngine_Debug
#endif

#ifndef kOggUseThread
//	#define kOggUseThread
#endif

typedef enum
{
	SOUND_THREAD_PRIORITY_VERY_LOW  = 15,	
	SOUND_THREAD_PRIORITY_LOW       = 23,
	SOUND_THREAD_PRIORITY_NORMAL    = 31,	
	SOUND_THREAD_PRIORITY_HIGH		= 39,
	SOUND_THREAD_PRIORITY_VERY_HIGH = 47
	
} SOUND_THREAD_PRIORITY;

static char* si_strupr(char *sz)
{
	char *p = sz;
	
	while (*p != '\0')
	{
		if(*p >= 'a' && *p <= 'z') *p -= 0x20;
		
		p++;
	}
	
	return sz;
}

static void msSleep( unsigned int _ms )
{
	int microsecs;
	
	struct timeval tv;
	
	microsecs = _ms * 1000;
	
	tv.tv_sec  = microsecs / 1000000;
	tv.tv_usec = microsecs % 1000000;
	
	select( 0, NULL, NULL, NULL, &tv );	
}

static int InitializeSource(unsigned int &u4Source)
{
	ALenum error = AL_NO_ERROR;
	
	ALfloat sourcePos[]={ 0.0, 0.0, 0.0};	// Front and right of the listener
	ALfloat sourceVel[]={ 0.0, 0.0, 0.0};
	alGetError();
	
	alSourcef(u4Source, AL_PITCH, 1.0f);
	if ((error = alGetError()) != AL_NO_ERROR)
	{
		printf("Error setting pitch of source: %d : %x\n", u4Source, error);
	}
	alSourcef(u4Source, AL_GAIN, 1.0f);
	if ((error = alGetError()) != AL_NO_ERROR)
	{
		printf("Error setting gain of source: %d : %x\n", u4Source, error);
	}
	alSourcef(u4Source, AL_REFERENCE_DISTANCE, 1.0f);
	if ((error = alGetError()) != AL_NO_ERROR)
	{
		printf("Error setting Reference Distance of source: %d : %x\n", u4Source, error);
	}
	alSourcef(u4Source, AL_ROLLOFF_FACTOR, 1.0f);
	if ((error = alGetError()) != AL_NO_ERROR)
	{
		printf("Error setting Rolloff of source: %d : %x\n", u4Source, error);
	}		
	alSourcefv(u4Source, AL_POSITION, sourcePos);	
	if ((error = alGetError()) != AL_NO_ERROR)
	{
		printf("Error setting position of source: %d : %x\n", u4Source, error);
	}
	alSourcefv(u4Source, AL_VELOCITY, sourceVel);	
	if ((error = alGetError()) != AL_NO_ERROR)
	{
		printf("Error setting velocity of source: %d : %x\n", u4Source, error);
	}
	/*	
	 alSourcei(u4Source, AL_LOOPING, AL_FALSE);
	 if ((error = alGetError()) != AL_NO_ERROR)
	 {
	 printf("Error setting looping of source: %d : %x\n", u4Source, error);
	 }
	 
	 alSourcei(u4Source, AL_BUFFER, u4Buffer);
	 if ((error = alGetError()) != AL_NO_ERROR)
	 {
	 printf("Error attaching buffer to source: %d : %x\n", u4Source, error);
	 return false;
	 }
	 */ 
	return 1;
}

CSoundBase *CSoundBase::CreateSound(const char *szFileName, bool bQueue /*= false*/)
{
	CSoundBase *pSound = NULL;
	assert(NULL != szFileName);
	if ( NULL == szFileName ) return pSound;
	
	std::string szTemp(szFileName);
	
	std::string::size_type pos = szTemp.find('.');
	if (std::string::npos == pos)
	{
		return pSound;
	}
	
	//	通过文件扩展名确定文件格式
	std::string szExt = szTemp.substr(pos + 1);
	//if ( strcmp(static_cast<const char*>(si_strupr(const_cast<char*>(szExt.c_str()))), "OGG") == 0 )
	if ( strcmp(si_strupr(const_cast<char*>(szExt.c_str())), "OGG") == 0 )
	{	//	OGG 格式
		pSound = new CSoundOgg();
	}
	else if ( strcmp(si_strupr(const_cast<char*>(szExt.c_str())), "CAF") == 0 )
	{	//	CAF 格式
		if ( bQueue )
		{
			pSound = new CSoundPCMQueue();
		}
		else
		{
			pSound = new CSoundPCM;
		}
	}
	else if ( strcmp(si_strupr(const_cast<char*>(szExt.c_str())), "WAV") == 0 )
	{	//	Wav 格式
		if ( bQueue )
		{
			pSound = new CSoundPCMQueue();
		}
		else
		{
			pSound = new CSoundPCM;
		}
	}
	else
	{
		return pSound;
	}
	
	if ( !pSound->Init(szFileName) )
	{
		delete pSound;
		pSound = NULL;
	}
	
	return pSound;
}

/*
 **************************************************************
 *					Class CSoundPCM
 **************************************************************
 */
CSoundPCM::CSoundPCM()
{
	
}

CSoundPCM::~CSoundPCM()
{
	if ( alIsSource(m_u4Source) )
	{
		alSourceStop(m_u4Source);
		alDeleteSources(1, &m_u4Source);
	}
	
	if ( alIsBuffer(m_u4Buffer) )
	{
		alDeleteBuffers(1, &m_u4Buffer);
	}
}

void CSoundPCM::Play(bool bLooping /*= false*/)
{
	m_bLooping = bLooping;
	alSourcei(m_u4Source, AL_LOOPING, bLooping ? AL_TRUE : AL_FALSE);
	alSourcePlay(m_u4Source);
}

void CSoundPCM::Stop()
{
	alSourceStop(m_u4Source);
}

void CSoundPCM::Pause()
{
	alSourcePause(m_u4Source);
}

int CSoundPCM::State()
{
	ALint state = 0;
	alGetSourcei(m_u4Source, AL_SOURCE_STATE, &state);
	m_i4State = state;
	return state;
}

int CSoundPCM::Init(const char *szFileName)
{
	assert(NULL != szFileName);
	if ( NULL == szFileName )
	{
		return 0;
	}

	ALuint u4Buffer;
	alGenBuffers(1, &u4Buffer);
	if ( !_initBuffer(szFileName, u4Buffer) )
	{
		return 0;
	}
	
	ALuint u4Source;
	alGenSources(1, &u4Source);	
	if ( !_initSource(u4Source) )
	{
		alDeleteBuffers(1, &u4Buffer);
		return 0;
	}
	
	if ( alIsSource(m_u4Source) )
	{
		alSourceStop(m_u4Source);
		alDeleteSources(1, &m_u4Source);
	}
	
	if ( alIsBuffer(m_u4Buffer) )
	{
		alDeleteBuffers(1, &m_u4Buffer);
	}
	
	alSourcei(u4Source, AL_BUFFER, u4Buffer);
	
	m_u4Source = u4Source;
	m_u4Buffer = u4Buffer;
	
	return 1;
}

int CSoundPCM::_initBuffer(const char *szFileName, ALuint &u4Buffer)
{
	int i4Ret = 0;
	NSString *strFileName = [[NSString alloc] initWithFormat:@"%s",szFileName];
	NSRange range = [strFileName rangeOfString:@"."];
	
	if ( range.location == NSNotFound || (range.location + range.length) >= [strFileName length] )
	{
		[strFileName release];
		return i4Ret;
	}
	
	NSString *strName = [strFileName substringWithRange:NSMakeRange(0, range.location)];
	NSString *strExtName = [strFileName substringWithRange:NSMakeRange(range.location + 1, [strFileName length] - range.location - 1)];
	
	ALenum error = AL_NO_ERROR;
	ALenum format;
	ALvoid *data;
	ALsizei size;
	ALsizei freq;
	
	NSBundle *bundle = [NSBundle mainBundle];
	CFURLRef fileURL;
	
	//	声音文件加载
	fileURL = (CFURLRef)[[NSURL fileURLWithPath:[bundle pathForResource:strName ofType:strExtName]] retain];
	[strFileName release];
	
	if (fileURL)
	{
		//data = GetOpenALAudioData(fileURL, &size, &format, &freq);
		data = GetOpenALAudioData(fileURL, &size, &format, &freq);
		if ( NULL == data )
		{
			printf("Error GetOpenALAudioData() return NULL!\n");
			return i4Ret;
		}
		CFRelease(fileURL);
		
		alBufferData(u4Buffer, format, data, size, freq);
		//alBufferDataStaticProc(u4Buffer, format, data, size, freq);
		
		free(data);
		if ((error = alGetError()) != AL_NO_ERROR)
		{
			printf("error attaching audio to buffer: %d:%x\n", u4Buffer, error);
			return i4Ret;
		}
		i4Ret = 1;
	}
	else {
		printf("Could not find file! %d\n", u4Buffer);
	}
	return i4Ret;
}

int CSoundPCM::_initSource(ALuint &u4Source)
{
	return InitializeSource(u4Source);
}

void CSoundPCM::Release()
{
	if( alIsSource(m_u4Source) )
	{
		alSourceStop(m_u4Source);
		alDeleteSources(1, &m_u4Source);
	}
	
	if ( alIsBuffer(m_u4Buffer) )
	{
		alDeleteBuffers(1, &m_u4Buffer);
	}
}


/*
 **************************************************************
 *					Class CSoundPCMQueue
 **************************************************************
 */
CSoundPCMQueue::CSoundPCMQueue():
	m_AudioFileID(0),
	m_i4Size(0), 
	m_i4Format(0), 
	m_i4Rate(0),
	m_i4Offset(0),
	m_bEnded(true)
{
	
}

CSoundPCMQueue::~CSoundPCMQueue()
{
	if ( alIsSource(m_u4Source) )
	{
		alSourceStop(m_u4Source);
		alDeleteSources(1, &m_u4Source);
	}
	
	if ( alIsBuffer(m_u4Buffers[0]) )
	{
		alDeleteBuffers(MAX_BUFFER_NUM, m_u4Buffers);
	}
	
	if (m_AudioFileID) AudioFileClose(m_AudioFileID);
}

void CSoundPCMQueue::Play(bool bLooping /*= false*/)
{
	m_bLooping = bLooping;
	alSourcei(m_u4Source, AL_LOOPING, AL_FALSE);
	alSourcePlay(m_u4Source);
	this->State();
	m_bEnded = false;
}

void CSoundPCMQueue::Stop()
{
	m_bEnded = true;
	alSourceStop(m_u4Source);
}

void CSoundPCMQueue::Pause()
{
	m_bEnded = true;
	alSourcePause(m_u4Source);
}

int CSoundPCMQueue::State()
{
	ALint state = 0;
	alGetSourcei(m_u4Source, AL_SOURCE_STATE, &state);
	m_i4State = state;
	return state;
}

int CSoundPCMQueue::Init(const char *szFileName)
{
	assert(NULL != szFileName);
	if ( NULL == szFileName )
	{
		return 0;
	}
	
	ALuint u4Buffers[MAX_BUFFER_NUM];
	alGenBuffers(MAX_BUFFER_NUM, u4Buffers);
	int i4NumberOfBuffer = MAX_BUFFER_NUM;
	if ( !_initBuffer(szFileName, u4Buffers, i4NumberOfBuffer) )
	{
		return 0;
	}
	
	ALuint u4Source;
	alGenSources(1, &u4Source);	
	if ( !_initSource(u4Source) )
	{
		alDeleteBuffers(MAX_BUFFER_NUM, u4Buffers);
		return 0;
	}
	
	if ( alIsSource(m_u4Source) )
	{
		alSourceStop(m_u4Source);
		alDeleteSources(1, &m_u4Source);
	}
	
	if ( alIsBuffer(m_u4Buffers[0]) )
	{
		alDeleteBuffers(MAX_BUFFER_NUM, m_u4Buffers);
	}
	
	alSourceQueueBuffers(u4Source, i4NumberOfBuffer, u4Buffers);
	m_u4Source = u4Source;
	
	memcpy(m_u4Buffers, u4Buffers, sizeof(ALuint) * MAX_BUFFER_NUM);
	
	return 1;
}

int CSoundPCMQueue::_initBuffer(const char *szFileName, ALuint *u4Buffers, int &i4NumberOfBuffer)
{
	int i4Ret = 0;
	NSString *strFileName = [[NSString alloc] initWithFormat:@"%s",szFileName];
	NSRange range = [strFileName rangeOfString:@"."];
	
	if ( range.location == NSNotFound || (range.location + range.length) >= [strFileName length] )
	{
		[strFileName release];
		return i4Ret;
	}
	
	NSString *strName = [strFileName substringWithRange:NSMakeRange(0, range.location)];
	NSString *strExtName = [strFileName substringWithRange:NSMakeRange(range.location + 1, [strFileName length] - range.location - 1)];
	
	//ALenum error = AL_NO_ERROR;
	ALenum format;
	//ALvoid *data;
	ALsizei size;
	ALsizei freq;
	
	NSBundle *bundle = [NSBundle mainBundle];
	CFURLRef fileURL;
	
	//	声音文件加载
	fileURL = (CFURLRef)[[NSURL fileURLWithPath:[bundle pathForResource:strName ofType:strExtName]] retain];
	[strFileName release];
	
	AudioFileID afid = 0;
	int i4Offset = 0;
	
	if (fileURL)
	{
		if ( !_OpenAudioFile(fileURL, &afid, &size, &format, &freq) ) return i4Ret;
		int i;
		for(i = 0; i < i4NumberOfBuffer; ++i)
		{
			if ( !_ReadAudioData(u4Buffers[i], afid, &i4Offset, size, format, freq) ) break;
		}
		
		i4NumberOfBuffer = i;
		
		m_i4Rate	= freq;
		m_i4Format	= format;
		m_i4Size	= size;
		m_i4Offset	= i4Offset;
		m_AudioFileID = afid;
		
		i4Ret = 1;
	}
	else {
		printf("Could not find file!\n");
	}
	return i4Ret;
}

bool CSoundPCMQueue::_OpenAudioFile(CFURLRef inFileURL, AudioFileID *outAudioFileID, ALsizei *outDataSize, ALenum *outDataFormat, ALsizei *outSampleRate)
{
	OSStatus						err = noErr;	
	UInt64							fileDataSize = 0;
	AudioStreamBasicDescription		theFileFormat;
	UInt32							thePropertySize = sizeof(theFileFormat);
	AudioFileID						afid = 0;
	//void*							theData = NULL;
	bool							bRet = false;
	
	// Open a file with ExtAudioFileOpen()
	err = AudioFileOpenURL(inFileURL, kAudioFileReadPermission, 0, &afid);
	if(err) { printf("MyGetOpenALAudioData: AudioFileOpenURL FAILED, Error = %ld\n", err); goto Exit; }
	
	// Get the audio data format
	err = AudioFileGetProperty(afid, kAudioFilePropertyDataFormat, &thePropertySize, &theFileFormat);
	if(err) { printf("MyGetOpenALAudioData: AudioFileGetProperty(kAudioFileProperty_DataFormat) FAILED, Error = %ld\n", err); goto Exit; }
	
	if (theFileFormat.mChannelsPerFrame > 2)  { 
		printf("MyGetOpenALAudioData - Unsupported Format, channel count is greater than stereo\n"); goto Exit;
	}
	
	if ((theFileFormat.mFormatID != kAudioFormatLinearPCM) || (!TestAudioFormatNativeEndian(theFileFormat))) { 
		printf("MyGetOpenALAudioData - Unsupported Format, must be little-endian PCM\n"); goto Exit;
	}
	
	if ((theFileFormat.mBitsPerChannel != 8) && (theFileFormat.mBitsPerChannel != 16)) { 
		printf("MyGetOpenALAudioData - Unsupported Format, must be 8 or 16 bit PCM\n"); goto Exit;
	}
	
	
	thePropertySize = sizeof(fileDataSize);
	err = AudioFileGetProperty(afid, kAudioFilePropertyAudioDataByteCount, &thePropertySize, &fileDataSize);
	if(err) { printf("MyGetOpenALAudioData: AudioFileGetProperty(kAudioFilePropertyAudioDataByteCount) FAILED, Error = %ld\n", err); goto Exit; }
	
	*outAudioFileID	= afid;
	*outDataSize	= (ALsizei)fileDataSize;
	*outDataFormat	= (theFileFormat.mChannelsPerFrame > 1) ? AL_FORMAT_STEREO16 : AL_FORMAT_MONO16;
	*outSampleRate	= (ALsizei)theFileFormat.mSampleRate;
	
	bRet = true;
Exit:
	return bRet;
}

bool CSoundPCMQueue::_ReadAudioData(ALuint &u4Buffer, AudioFileID afid, int *pi4Offset, int i4TotalSize, int i4Format, int i4Rate)
{
	if ( 0 == afid ) return false;
	int i4DataSize = (i4TotalSize - *pi4Offset) > SOUND_BUFFER_SIZE ? SOUND_BUFFER_SIZE : (i4TotalSize - *pi4Offset);
	if ( i4DataSize <= 0 ) return false;
	
	OSStatus	err = noErr;
	char szBuffer[SOUND_BUFFER_SIZE];
	
#ifdef kAudioEngine_Debug	
	CFTimeInterval time1 = CFAbsoluteTimeGetCurrent();
#endif
	
	err = AudioFileReadBytes(afid, false, *pi4Offset, (UInt32*)&i4DataSize, (void*)szBuffer);

#ifdef kAudioEngine_Debug	
	CFTimeInterval time2 = CFAbsoluteTimeGetCurrent();
	CFTimeInterval time3 = time2 - time1;
	printf("size = %d, begin = %f, end = %f, interval = %f\n", i4DataSize, time1, time2, time3);
#endif
	
	if(err != noErr)
	{
		return false;
	}
	if( i4DataSize <= 0 ) return false;
	
	*pi4Offset += i4DataSize;
	
	alBufferData( u4Buffer, 
				 i4Format,
				 szBuffer,
				 i4DataSize,
				 i4Rate );
	return true;
}

void CSoundPCMQueue::Update(float dt)
{
	int p = 0,
		q = 0;
	
	if( AL_PLAYING != this->LastState() )
	{
		alGetSourcei( m_u4Source, AL_BUFFERS_QUEUED, &q );
		
		if( !q )
		{
			m_i4Offset = 0;
			
			for(int i = 0; i < MAX_BUFFER_NUM; ++i)
			{
				if ( !_ReadAudioData(m_u4Buffers[i], m_AudioFileID, &m_i4Offset, m_i4Size, m_i4Format, m_i4Rate) ) break;
				
				alSourceQueueBuffers( m_u4Source, 1, &m_u4Buffers[ i ] );
			}
		}
		else if ( !m_bEnded )
		{
			this->Play(m_bLooping);
		}
		
		return; 
	}
	
	this->State();
	
    alGetSourcei( m_u4Source, AL_BUFFERS_PROCESSED, &p );
	
	ALuint u4Curr;
    while( p )
    {
        alSourceUnqueueBuffers( m_u4Source,
							   1,
							   &u4Curr );
		
		if ( _ReadAudioData(u4Curr, m_AudioFileID, &m_i4Offset, m_i4Size, m_i4Format, m_i4Rate) )
		{
			alSourceQueueBuffers( m_u4Source,
								 1,
								 &u4Curr );
		}
		
		--p;
    }
	
	alGetSourcei( m_u4Source, AL_BUFFERS_QUEUED, &q );
	
	if( !q )
	{
		m_bEnded = true;
		m_i4Offset = 0;
		
		for(int i = 0; i < MAX_BUFFER_NUM; ++i)
		{
			if ( !_ReadAudioData(m_u4Buffers[i], m_AudioFileID, &m_i4Offset, m_i4Size, m_i4Format, m_i4Rate) ) break;
			alSourceQueueBuffers( m_u4Source, 1, &m_u4Buffers[ i ] );
		}
		
		if( !m_bLooping )
		{ 
			this->Stop();
		}
		else
		{ 
			this->Play(m_bLooping); 
		}
	}
}

int CSoundPCMQueue::_initSource(ALuint &u4Source)
{
	return InitializeSource(u4Source);
}

void CSoundPCMQueue::Release()
{
	if( alIsSource(m_u4Source) )
	{
		alSourceStop(m_u4Source);
		alDeleteSources(1, &m_u4Source);
	}
	
	if ( alIsBuffer(m_u4Buffers[0]) )
	{
		alDeleteBuffers(MAX_BUFFER_NUM, m_u4Buffers);
	}
	
	if (m_AudioFileID) 
	{
		AudioFileClose(m_AudioFileID);
		m_AudioFileID = 0;
	}
}


/*
 **************************************************************
 *					Class CSoundOgg
 **************************************************************
 */

CSoundOgg::CSoundOgg():
	m_i4ThreadState(OGG_THREAD_RUNING),
	m_priority(33),//SOUND_THREAD_PRIORITY_HIGH),
	m_u4Curr(0),
	m_u4NumberOfBuffer(0),
	m_bEnded(true)
{

}

CSoundOgg::~CSoundOgg()
{
	this->_ResourceFree();
}

int CSoundOgg::_ResourceFree()
{
#ifdef kOggUseThread	
	if ( OGG_THREAD_RUNING == m_i4ThreadState )
	{
		m_i4ThreadState = OGG_THREAD_STOPING;
		int i4Times = 200;
		while ( OGG_THREAD_STOPPED != m_i4ThreadState && i4Times-- > 0 )
		{
			msSleep(10);
		}
		
		pthread_join(m_thread, NULL);
	}
#endif	//	kOggUseThread
	
	if (alIsSource(m_u4Source))
	{
		alSourceStop(m_u4Source);
		alDeleteSources(1, &m_u4Source);
	}
	
	if (alIsBuffer(m_u4Buffers[0]))
	{
		alDeleteBuffers(MAX_BUFFER_NUM, m_u4Buffers);
	}
	
	return 1;
}

void CSoundOgg::Play(bool bLooping /*= false*/)
{
	if ( AL_PLAYING == this->State() ) return;
	
	m_bLooping = bLooping;
	
	alSourcei(m_u4Source, AL_LOOPING, AL_FALSE);
	alSourcePlay(m_u4Source);
	this->State();
	m_bEnded = false;
}

void CSoundOgg::Stop()
{
	m_bEnded = true;
	alSourceStop(m_u4Source);
}

void CSoundOgg::Pause()
{
	m_bEnded = true;
	alSourcePause(m_u4Source);
}

int CSoundOgg::State()
{
	ALint state = 0;
	alGetSourcei(m_u4Source, AL_SOURCE_STATE, &state);
	m_i4State = state;
	return state;
}

int CSoundOgg::Init(const char *szFileName)
{
	assert(NULL != szFileName);
	if ( NULL == szFileName )
	{
		return 0;
	}
	
	ALuint u4Buffers[MAX_BUFFER_NUM];
	alGenBuffers(MAX_BUFFER_NUM, u4Buffers);
	if ( !_LoadOgg(szFileName, u4Buffers, MAX_BUFFER_NUM) )
	{
		return 0;
	}
	
	ALuint u4Source;
	alGenSources(1, &u4Source);	
	if ( !InitializeSource(u4Source) )
	{
		alDeleteBuffers(MAX_BUFFER_NUM, u4Buffers);
		return 0;
	}
	
	if ( alIsSource(m_u4Source) )
	{
		alSourceStop(m_u4Source);
		alDeleteSources(1, &m_u4Source);
	}
	
	if ( alIsBuffer(m_u4Buffers[0]) )
	{
		alDeleteBuffers(MAX_BUFFER_NUM, m_u4Buffers);
	}
	
	alSourceQueueBuffers(u4Source, m_u4NumberOfBuffer, u4Buffers);
	
	m_u4Source = u4Source;
	
	memcpy(m_u4Buffers, u4Buffers, sizeof(ALuint) * MAX_BUFFER_NUM);

#ifdef kOggUseThread	
	m_i4ThreadState = OGG_THREAD_RUNING;
	
	m_thread_hdl = pthread_create( &m_thread,
									NULL,
									PlayThread,
									(void *)this );	
	msSleep( 100 );
#endif	//	kOggUseThread
	
	return 1;
}

void* CSoundOgg::PlayThread( void *_ptr )
{
	CSoundOgg *pSound = (CSoundOgg*)_ptr;
	bool bRuning = true;
	
	struct sched_param param;
	
	param.sched_priority = pSound->m_priority;
	pthread_setschedparam( pSound->m_thread, SCHED_RR, &param );
	
	while (bRuning) {
		switch (pSound->m_i4ThreadState) {
			case OGG_THREAD_RUNING:
			{	
				pSound->_UpdateBufferStream();
				
				break;
			}
			case OGG_THREAD_STOPING:
			{
				bRuning = false;
				break;
			}
			default:
				break;
		}
		
		//msSleep(100);
		usleep(50);
	}
	pSound->m_i4ThreadState = OGG_THREAD_STOPPED;

	return NULL;
}

void CSoundOgg::Update(float dt)
{
#ifndef kOggUseThread	
	this->_UpdateBufferStream();
#endif
	
}



int CSoundOgg::_GetBufferStream( unsigned int u4Buffer )
{
	char pcm[ SOUND_BUFFER_SIZE ] = {""};
	
	int		size = 0,
			bit;
	int		readLen = 0;
	
#ifdef kAudioEngine_Debug	
	CFTimeInterval time1 = CFAbsoluteTimeGetCurrent();
#endif
	
	while( size < SOUND_BUFFER_SIZE )
	{
		readLen = (SOUND_BUFFER_SIZE - size) > 512 ? 512 : (SOUND_BUFFER_SIZE - size);
		
		int r = ov_read( &m_OggFile,
						pcm + size,
						readLen,//SOUND_BUFFER_SIZE - size,
						0, 2, 1,
						&bit );

		if( r > 0 )
		{ size += r; }
		else
		{ break; }
		//msSleep(5);
	}
	
#ifdef kAudioEngine_Debug	
	CFTimeInterval time2 = CFAbsoluteTimeGetCurrent();
	CFTimeInterval timeInterval = time2 - time1;
	printf("readVal = %d, time1 = %f, time2 = %f, interval = %f\n", size, time1, time2, timeInterval);
#endif	
	
	if( !size )
	{ return 0; }
	
	alBufferData( u4Buffer, 
				 m_u4Format,
				 pcm,
				 size,
				 m_u4Rate );
	
	return 1;
}

int CSoundOgg::_LoadOgg(const char *szFileName, unsigned int *u4Buffers, int i4BufferNum)
{	
	NSBundle *thisBundle = [NSBundle mainBundle];
	NSString *strFile = [[NSString alloc] initWithFormat:@"%@/%s", [thisBundle resourcePath], szFileName];
	FILE *file = NULL;
	file = fopen([strFile UTF8String], "rb");
	[strFile release];
	if ( NULL == file )
	{
		printf("Error Open Ogg file!\n");
		return 0;
	}
	
	ov_open(file, &m_OggFile, NULL, 0);
	m_pinfo = ov_info(&m_OggFile, -1);
	
	if( m_pinfo->channels == 1 )
	{ 
		m_u4Format = AL_FORMAT_MONO16; 
	}
	else
	{ 
		m_u4Format = AL_FORMAT_STEREO16; 
	}
	
	m_u4Size = ( ov_pcm_total( &m_OggFile, -1 ) * m_pinfo->channels << 1 );
	m_u4Rate = m_pinfo->rate;
	
	for (int i = 0; i < i4BufferNum; ++i)
	{
		if (!_GetBufferStream(u4Buffers[i])) break;
		++m_u4NumberOfBuffer;
	}
	
	return m_u4NumberOfBuffer ? 1 : 0;
}

int CSoundOgg::_UpdateBufferStream()
{
	int p = 0,
		q = 0;
	
	if( AL_PLAYING != this->LastState() )
	{
		alGetSourcei( m_u4Source, AL_BUFFERS_QUEUED   , &q );
		
		if( !q )
		{
			ov_pcm_seek(&m_OggFile, 0);
			
			for(int i = 0; i < MAX_BUFFER_NUM; ++i)
			{
				if ( !_GetBufferStream( m_u4Buffers[ i ] ) ) break;
				alSourceQueueBuffers( m_u4Source, 1, &m_u4Buffers[ i ] );
			}
		}
		else if ( !m_bEnded )
		{
			this->Play(m_bLooping);
		}
		
		return 0; 
	}
	
	this->State();
	
    alGetSourcei( m_u4Source, AL_BUFFERS_PROCESSED, &p );
	//printf("p ----------------> %d\n", p);
	
    while( p )
    {
        alSourceUnqueueBuffers( m_u4Source,
							   1,
							   &m_u4Curr );
		
		if( _GetBufferStream( m_u4Curr ) )
		{
			alSourceQueueBuffers( m_u4Source,
								 1,
								 &m_u4Curr );
		}
		
		--p;
    }
	
	alGetSourcei( m_u4Source, AL_BUFFERS_QUEUED   , &q );
	
	//printf("q =================> %d\n", p);
	
	if( !q )
	{
		//printf("q 00000000000000000\n");
		
		m_bEnded = true;
		
		ov_pcm_seek(&m_OggFile, 0);
		
		for(int i = 0; i < MAX_BUFFER_NUM; ++i)
		{
			if ( !_GetBufferStream( m_u4Buffers[ i ] ) ) break;
			alSourceQueueBuffers( m_u4Source, 1, &m_u4Buffers[ i ] );
		}
	
		if( !m_bLooping )
		{ 
			this->Stop();
		}
		else
		{ 
			this->Play(m_bLooping); 
		}
	}
	
	return 1;
}

void CSoundOgg::Release()
{
	this->_ResourceFree();
}
